from PyQt5.QtCore import QObject, pyqtSignal, QRunnable
import feedparser
from datetime import datetime
from bs4 import BeautifulSoup
import concurrent.futures
from rss_reader.database.models import Article, Feed
from rss_reader.logger import logger
from functools import partial
from typing import Optional
from concurrent.futures import Future
from concurrent.futures import ThreadPoolExecutor
import concurrent.futures
import requests

class FeedUpdateSignals(QObject):
    """定义工作线程的信号"""
    progress = pyqtSignal(int, int)  # current, total
    finished = pyqtSignal()
    error = pyqtSignal(str)


class FeedUpdater(QRunnable):
    """RSS订阅更新工作线程"""

    def __init__(self, session_factory, feed_ids, max_workers=4):
        super().__init__()
        self.session_factory = session_factory
        self.feed_ids = feed_ids
        self.max_workers = max_workers
        self.signals = FeedUpdateSignals()
        logger.debug(f"创建FeedUpdater, 需要更新 {len(feed_ids)} 个订阅")

    def run(self):
        """执行并发更新任务

        使用ThreadPoolExecutor并行更新多个订阅源，
        并通过信号机制报告进度。
        """
        try:
            total = len(self.feed_ids)
            logger.info(f"开始更新 {total} 个订阅源")

            with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
                futures = []

                for i, feed_id in enumerate(self.feed_ids):
                    future = executor.submit(self._update_single_feed, feed_id)

                    # 使用partial绑定参数
                    callback = partial(
                        self._handle_progress_update,
                        i=i,
                        total=total
                    )
                    future.add_done_callback(callback)
                    futures.append(future)

                concurrent.futures.wait(futures)

            self.signals.finished.emit()

        except Exception as e:
            logger.error(f"更新任务失败: {str(e)}", exc_info=True)
            self.signals.error.emit(str(e))

    def _handle_progress_update(self, future: Future, i: int, total: int) -> None:
        """处理进度更新的回调"""
        try:
            future.result()  # 确保捕获任务中的异常
            self.signals.progress.emit(i + 1, total)
        except Exception as e:
            logger.warning(f"任务进度更新失败: {str(e)}")

    def _update_single_feed(self, feed_id):
        """更新单个订阅源 - 线程安全版本"""
        session = self.session_factory()
        try:
            # 禁用自动flush避免锁定问题
            with session.no_autoflush:
                feed = session.query(Feed).get(feed_id)
                if not feed:
                    logger.warning(f"未找到ID为 {feed_id} 的订阅")
                    return

                logger.debug(f"开始更新订阅: {feed.title} (ID: {feed.id})")

                # 解析RSS源
                parsed = feedparser.parse(feed.url)
                if parsed.get('bozo', 0) == 1:
                    logger.warning(f"解析订阅 {feed.url} 时出错: {parsed.bozo_exception}")
                    return

                # 更新订阅元数据
                feed.title = parsed.feed.get('title', feed.title)
                feed.last_updated = datetime.now()

                # 尝试获取图标
                ''''
                if not feed.icon_url:
                    icon_url = parsed.feed.get('icon') or parsed.feed.get('image', {}).get('href')
                    if icon_url:
                        feed.icon_url = icon_url
                '''

                new_articles = 0
                for entry in parsed.entries:
                    link = entry.get('link', '')
                    if not link:
                        continue

                    try:
                        # 检查文章是否已存在 - 使用更安全的查询方式
                        exists = session.query(
                            session.query(Article)
                            .filter_by(url=link)
                            .exists()
                        ).scalar()

                        if not exists:
                            # 提取文章内容
                            soup = BeautifulSoup(entry.get('description', ''), 'html.parser')
                            summary = soup.get_text()
                            try:
                                response = requests.get(link, timeout=5)
                                content = str(soup.find('article')) or str(soup.find('main')) or response.text
                            except Exception as e:
                                content = entry.get('description', '')
                            # 处理发布时间
                            published = None
                            if hasattr(entry, 'published_parsed'):
                                published = datetime(*entry.published_parsed[:6])
                            elif hasattr(entry, 'updated_parsed'):
                                published = datetime(*entry.updated_parsed[:6])

                            # 创建新文章
                            article = Article(
                                title=entry.get('title', '无标题'),
                                url=link,
                                summary=summary,
                                content=content,
                                published=published,
                                feed_id=feed.id
                            )
                            session.add(article)
                            new_articles += 1

                    except Exception as e:
                        logger.warning(f"处理文章条目时出错: {str(e)}")
                        session.rollback()
                        continue

                # 显式提交变更
                session.commit()
                logger.info(f"更新订阅 {feed.title} 完成, 新增 {new_articles} 篇文章")

        except Exception as e:
            logger.error(f"更新订阅 {feed.title if 'feed' in locals() else feed_id} 失败: {str(e)}", exc_info=True)
            session.rollback()
        finally:
            session.close()  # 确保会话关闭